package com.minelx.statementsplus.cha.base;

import java.io.IOException;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Types;
import java.util.stream.IntStream;

import static java.util.stream.Collectors.joining;

public class ResultSetWrapper {
	private final ResultSet resultSet;

	public ResultSetWrapper(ResultSet resultSet) {
		this.resultSet = resultSet;
	}

	public int writeThrough(OutputStream outputStream) {
		try {
			return writeThrough0(outputStream);
		} catch (SQLException e) {
			throw new RuntimeException("error while requesting sql query.", e);
		} catch (IOException e) {
			throw new RuntimeException("error while writing file.", e);
		}
	}

	private int writeThrough0(OutputStream outputStream) throws SQLException, IOException {
		// fetching column names
		ResultSetMetaData metaData = resultSet.getMetaData();
		int columnCounts = metaData.getColumnCount();

		// writing title
		outputStream.write(IntStream.rangeClosed(1, columnCounts)
				.mapToObj(index -> columnLabel(metaData, index))
				.collect(joining(","))
				.getBytes(StandardCharsets.UTF_8));

		// fetching type of columns
		int[] columnTypes = IntStream.rangeClosed(1, columnCounts)
				.map(index -> columnType(metaData, index))
				.toArray();

		// writing content
		int lineCounts = 0;
		while (resultSet.next()) {
			// push a line separator
			outputStream.write("\n".getBytes(StandardCharsets.UTF_8));
			for (int columnAt = 0; columnAt < columnCounts; columnAt++) {
				String cellAsText = asString(resultSet, columnAt + 1, columnTypes[columnAt]);
				// write out
				outputStream.write(cellAsText.getBytes(StandardCharsets.UTF_8));
				if (columnAt != columnCounts - 1) { // append comma if not ended
					outputStream.write(",".getBytes(StandardCharsets.UTF_8));
				}
			}
			lineCounts++;
		}
		return lineCounts;
	}

	static String asString(ResultSet resultSet, int columnAt, int columnType) throws SQLException {
		if (Types.INTEGER == columnType) {
			return String.valueOf(resultSet.getInt(columnAt));
		} else if (Types.BIGINT == columnType) {
			return String.valueOf(resultSet.getInt(columnAt));
		} else if (Types.DECIMAL == columnType) {
			return String.valueOf(resultSet.getBigDecimal(columnAt));
		} else if (Types.VARCHAR == columnType) {
			return resultSet.getString(columnAt);
		} else if (Types.TINYINT == columnType) {
			return String.valueOf(resultSet.getInt(columnAt));
		} else {
			throw new IllegalStateException("can't recognize sql type: " + columnType);
		}
	}

	static int columnType(ResultSetMetaData metaData, int index) {
		try {
			return metaData.getColumnType(index);
		} catch (SQLException e) {
			throw new RuntimeException("error while getting column type at: " + index, e);
		}
	}

	static String columnLabel(ResultSetMetaData metaData, int index) {
		try {
			return metaData.getColumnLabel(index);
		} catch (SQLException e) {
			throw new RuntimeException("error while getting column label at: " + index, e);
		}
	}
}
